﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp;
using Abp.Authorization;
using Abp.AutoMapper;
using Abp.Domain.Repositories;
using Abp.ObjectMapping;
using Abp.Runtime.Caching;
using Abp.UI;
using AutoMapper.QueryableExtensions;
using FwProject.Core.Restaurant;
using FwProject.Core.SettlementInfoModel;
using FwProject.Restaurant.Dto;
using FwProject.Users;
using X.PagedList;

namespace FwProject.Restaurant
{
    [AbpAuthorize]
    public class DepositAppService : FwProjectAppServiceBase, IDepositAppService
    {
        public readonly IRepository<DepositInfo, Guid> _repositoryDeposit;
        public readonly IRepository<RestaurantInfo, Guid> _repositoryRestaurantInfo;
        public readonly IRepository<SettlementAccount, Guid> _repositorySettlementAccount;
        private readonly IRestaurantAppService _restaurantAppService;
        private readonly IUserAppService _userAppService;
        private readonly IRestaurantUserAppService _restaurantUserAppService;
        public readonly ICacheManager _catchmanager;
        public IGuidGenerator _guid { get; set; }
        public IObjectMapper _map { get; set; }

        public DepositAppService(IRepository<DepositInfo, Guid> repositoryDeposit, ICacheManager catchmanager,
            IUserAppService userAppService, IRestaurantUserAppService restaurantUserAppService, IRepository<RestaurantInfo, Guid> repositoryRestaurantInfo, IRestaurantAppService restaurantAppService,
            IRepository<SettlementAccount, Guid> repositorySettlementAccount)
        {
            _repositoryRestaurantInfo = repositoryRestaurantInfo;
            _repositoryDeposit = repositoryDeposit;
            _userAppService = userAppService;
            _catchmanager = catchmanager;
            _restaurantUserAppService = restaurantUserAppService;
            _guid = SequentialGuidGenerator.Instance;
            _repositorySettlementAccount = repositorySettlementAccount;
            _restaurantAppService = restaurantAppService;
        }

        public async Task<Guid> Create(DepositInput input)
        {
            var usermodel = await GetCurrentUserAsync();
            var restaurant = (await GetCache).AsQueryable().Where(o => o.RestaurantId == input.RestaurantId && o.FactoryinfoId == usermodel.FactoryinfoId).FirstOrDefault();
            if (restaurant != null)
                throw new UserFriendlyException("该餐厅已收押金！");

            var user = await _userAppService.GetByIdUserAsync(input.AgentPeoperUserId);
            var account = await _repositorySettlementAccount.GetAsync(input.CapitalAccountId);
            var model = _map.Map<DepositInfo>(input);
            model.Id = _guid.Create();
            model.DepositTime = input.DepositTime;
            model.AgentPeoper = user.Name;
            model.DepositType = DepositType.收押金;
            model.AccountNo = account.Number;
            model.AccountName = account.Name;

            var r = await _repositoryRestaurantInfo.GetAsync(model.RestaurantId);
            r.Deposit = r.Deposit + model.Money;

            //审核通过后才为有效数据
            //await _restaurantAppService.EditDeposit(input.RestaurantId,input.Money);
            return await _repositoryDeposit.InsertAndGetIdAsync(model);
        }
        /// <summary>
        /// 退押金
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<Guid> CreateReturnDeposit(DepositInput input)
        {
            //该餐厅已收金额
            var s = _repositoryDeposit.GetAll().Where(o => o.RestaurantId == input.RestaurantId && o.DepositType == DepositType.收押金).Sum(o => o.Money);
            //该餐厅已退金额
            var t = _repositoryDeposit.GetAll().Where(o => o.RestaurantId == input.RestaurantId && o.DepositType == DepositType.退押金).Sum(o => o.Money);

            if (s < t + input.Money)//判断已收小于已退金额
                throw new UserFriendlyException(200, $"押金退还失败！当前餐厅押金可退金额为{s - t}");

            var user = await _userAppService.GetByIdUserAsync(input.AgentPeoperUserId);
            var account = await _repositorySettlementAccount.GetAsync(input.CapitalAccountId);
            var model = _map.Map<DepositInfo>(input);
            model.Id = _guid.Create();
            model.DepositTime = input.DepositTime;
            model.AgentPeoper = user.Name;
            model.DepositType = DepositType.退押金;
            model.AccountNo = account.Number;
            model.AccountName = account.Name;

            var r = await _repositoryRestaurantInfo.GetAsync(model.RestaurantId);
            r.Deposit = r.Deposit - model.Money;


            return await _repositoryDeposit.InsertAndGetIdAsync(model);

            //需要审核通过后才有效
            //await _restaurantAppService.ReturnDeposit(input.RestaurantId, input.Money);
        }

        /// <summary>
        /// 判断该餐厅是否已经有押金数据
        /// </summary>
        /// <param name="Id"></param>
        /// <returns></returns>
        public bool GetRestaurantByDeposit(Guid Id)
        {
            var model = _repositoryDeposit.GetAll().Where(o => o.RestaurantId == Id).FirstOrDefault();
            if (model != null)
                return true;

            return false;
        }

        public DepositDto GetRestaurantIdByDeposit(Guid Id)
        {
            var model = _repositoryDeposit.GetAll().Where(o => o.RestaurantId == Id).FirstOrDefault();
            return model.MapTo<DepositDto>();
        }
        /// <summary>
        /// 修改押金数据
        /// </summary>
        /// <param name="id"></param>
        /// <param name="input"></param>
        /// <returns></returns>

        public async Task Update(Guid id, DepositInput input)
        {
            var user = await _userAppService.GetByIdUserAsync(input.AgentPeoperUserId);
            var account = await _repositorySettlementAccount.GetAsync(input.CapitalAccountId);
            DepositInfo model = await _repositoryDeposit.GetAsync(id);
            var originalMoney = model.Money;

            if (model.DepositType == DepositType.收押金)
            {
                //总的可用押金
                var sumDepositQuery = _repositoryDeposit.GetAll().Where(o => o.RestaurantId == input.RestaurantId);
                //收押金(加上修改后的押金)
                var s = sumDepositQuery.Where(o => o.DepositType == DepositType.收押金 && o.Id != model.Id).Sum(o => o.Money) + input.Money;
                //退押金
                var t = sumDepositQuery.Where(o => o.DepositType == DepositType.退押金).Sum(o => o.Money);

                if (s < t)//已收押金小于退押金
                    throw new UserFriendlyException(200, "押金收取金额不能小于押金退还金额！");
            }
            else
            {
                //总的可用押金
                var sumDepositQuery = _repositoryDeposit.GetAll().Where(o => o.RestaurantId == input.RestaurantId);
                //收押金
                var s = sumDepositQuery.Where(o => o.DepositType == DepositType.收押金).Sum(o => o.Money);
                //退押金(加上修改后的押金)
                var t = sumDepositQuery.Where(o => o.DepositType == DepositType.退押金 && o.Id != model.Id).Sum(o => o.Money) + input.Money;

                if (s < t)//已收押金小于退押金
                    throw new UserFriendlyException(200, "押金收取金额不能小于押金退还金额！");
            }

            //DepositInfo deposit = new DepositInfo() {
            //    Id = model.Id,
            //    RestaurantId = input.RestaurantId,
            //    FactoryinfoId = input.FactoryinfoId,
            //    Money = input.Money,
            //    DepositType = model.DepositType,
            //    DepositStatus = model.DepositStatus,
            //    RemarkContent = input.RemarkContent,
            //    CreationTime = input.CreationTime,
            //    CreationNumber = model.CreationNumber,
            //    AgentPeoperUserId = input.AgentPeoperUserId,
            //    AgentPeoper = user.Name,
            //    CapitalAccountId = input.CapitalAccountId,
            //    AccountNo = account.Number,
            //    AccountName = account.Name
            //};

            input.MapTo(model);

            if (model.DepositType == DepositType.收押金)
            {

                var r = await _repositoryRestaurantInfo.GetAsync(model.RestaurantId);
                r.Deposit = r.Deposit + (model.Money - originalMoney);
            }
            else
            {
                var r = await _repositoryRestaurantInfo.GetAsync(model.RestaurantId);
                r.Deposit = r.Deposit - (model.Money - originalMoney);
            }


            model.AccountName = account.Name;
            model.AccountNo = account.Number;

            await _repositoryDeposit.UpdateAsync(model);
        }

        /// <summary>
        /// 收押金（分页）
        /// </summary>
        /// <returns></returns>
        public async Task<IPagedList<DepositDto>> GetCollectDepositPagedListAsync(int pageIdx, int pageSize, DateTime? beginTime, DateTime? endTime, DepositStatus? depositStatus, string restaurantName)
        {
            var factoryinfoId = (await base.GetCurrentUserAsync()).FactoryinfoId;
            var model = (await GetCache).AsQueryable().Where(o => o.DepositType == DepositType.收押金 && o.FactoryinfoId == factoryinfoId);
            var account = _repositorySettlementAccount.GetAll();
            var data = model.Join(account, a => a.CapitalAccountId, b => b.Id, (a, b) => new DepositDto
            {
                Id = a.Id,
                FactoryinfoId = a.FactoryinfoId,
                DepositStatus = a.DepositStatus,
                RestaurantName = a.RestaurantInfoModel.RestaurantName,
                RestaurantId = a.RestaurantId,
                DepositTime = a.DepositTime,
                CreationNumber = a.CreationNumber,
                AgentPeoper = a.AgentPeoper,
                AgentPeoperUserId = a.AgentPeoperUserId.Value,
                CapitalAccountId = a.CapitalAccountId.Value,
                AccountNo = b.Number,
                AccountName = b.Name,
                Money = a.Money,
                RemarkContent = a.RemarkContent
            });

            if (beginTime.HasValue)
                data = data.Where(o => o.DepositTime >= beginTime);
            if (endTime.HasValue)
                data = data.Where(o => o.DepositTime <= endTime.Value.AddHours(24));

            if (depositStatus.HasValue)
                data = data.Where(o => o.DepositStatus == depositStatus);


            if (!string.IsNullOrEmpty(restaurantName))
                data = data.Where(o => o.RestaurantName.Contains(restaurantName));

            return await data.OrderByDescending(o => o.DepositTime).ThenBy(o => o.DepositStatus).ToPagedListAsync(pageIdx, pageSize);
        }

        /// <summary>
        /// 押金
        /// </summary>
        /// <param name="beginTime"></param>
        /// <param name="endTime"></param>
        /// <param name="depositStatus"></param>
        /// <param name="restaurantName"></param>
        /// <returns></returns>
        public async Task<List<DepositDto>> GetCollectDepositListAsync(DateTime? beginTime, DateTime? endTime, DepositType? type, DepositStatus? depositStatus, string restaurantName)
        {
            var factoryinfoId = (await base.GetCurrentUserAsync()).FactoryinfoId;
            var model = (await GetCache).AsQueryable().Where(o => o.DepositType == type && o.FactoryinfoId == factoryinfoId);
            var account = _repositorySettlementAccount.GetAll();
            var data = model.Join(account, a => a.CapitalAccountId, b => b.Id, (a, b) => new DepositDto
            {
                Id = a.Id,
                FactoryinfoId = a.FactoryinfoId,
                DepositStatus = a.DepositStatus,
                RestaurantName = a.RestaurantInfoModel.RestaurantName,
                RestaurantId = a.RestaurantId,
                DepositTime = a.DepositTime,
                CreationNumber = a.CreationNumber,
                AgentPeoper = a.AgentPeoper,
                AgentPeoperUserId = a.AgentPeoperUserId.Value,
                CapitalAccountId = a.CapitalAccountId.Value,
                AccountNo = b.Number,
                AccountName = b.Name,
                Money = a.Money,
                RemarkContent = a.RemarkContent
            });

            if (beginTime.HasValue)
                data = data.Where(o => o.DepositTime >= beginTime);
            if (endTime.HasValue)
                data = data.Where(o => o.DepositTime <= endTime.Value.AddHours(24));

            if (depositStatus.HasValue)
                data = data.Where(o => o.DepositStatus == depositStatus);


            if (!string.IsNullOrEmpty(restaurantName))
                data = data.Where(o => o.RestaurantName.Contains(restaurantName));

            return await data.OrderByDescending(o => o.DepositTime).ThenBy(o => o.DepositStatus).ToListAsync();
        }


        /// <summary>
        /// 退押金（分页）
        /// </summary>
        /// <returns></returns>
        public async Task<IPagedList<DepositDto>> GetReturnDepositPagedListAsync(int pageIdx, int pageSize, DateTime? beginTime, DateTime? endTime, DepositStatus? depositStatus, string RestaurantName)
        {
            var model = (await GetCache).AsQueryable().Where(o => o.DepositType == DepositType.退押金);
            var account = _repositorySettlementAccount.GetAll();
            var data = model.Join(account, a => a.CapitalAccountId, b => b.Id, (a, b) => new DepositDto
            {
                Id = a.Id,
                FactoryinfoId = a.FactoryinfoId,
                DepositStatus = a.DepositStatus,
                RestaurantName = a.RestaurantInfoModel.RestaurantName,
                RestaurantId = a.RestaurantId,
                DepositTime = a.DepositTime,
                CreationNumber = a.CreationNumber,
                AgentPeoper = a.AgentPeoper,
                AgentPeoperUserId = a.AgentPeoperUserId.Value,
                CapitalAccountId = a.CapitalAccountId.Value,
                AccountNo = b.Number,
                AccountName = b.Name,
                Money = a.Money,
                RemarkContent = a.RemarkContent
            });

            if (beginTime.HasValue)
                data = data.Where(o => o.DepositTime >= beginTime);
            if (endTime.HasValue)
                data = data.Where(o => o.DepositTime <= endTime.Value.AddHours(24));

            if (!string.IsNullOrEmpty(RestaurantName))
                data = data.Where(o => o.RestaurantName.Contains(RestaurantName));

            if (depositStatus.HasValue)
                data = data.Where(o => o.DepositStatus == depositStatus);

            return await data.ToPagedListAsync(pageIdx, pageSize);
        }





        public DepositDto GetById(Guid id)
        {
            var model = _repositoryDeposit.GetAllIncluding(o => o.RestaurantInfoModel).Where(o => o.Id == id).FirstOrDefault();

            return _map.Map<DepositDto>(model);
        }

        /// <summary>
        /// 获取餐厅可修改押金金额
        /// </summary>
        /// <param name="RestaurantId"></param>
        /// <param name="depositType"></param>
        /// <returns></returns>
        public async Task<decimal> GetRestaurantModifiableAmountAsync(Guid RestaurantId, Guid depositId, DepositType depositType)
        {
            var user = await base.GetCurrentUserAsync();

            var model = _repositoryDeposit.GetAll().Where(o => o.FactoryinfoId == user.FactoryinfoId && o.RestaurantId == RestaurantId);

            decimal pice = 0m;
            decimal s = 0m;
            decimal t = 0m;
            switch (depositType)
            {
                case DepositType.收押金:
                    //已收押金
                    s = model.Where(o => o.DepositType == DepositType.收押金 && o.Id != depositId).Sum(o => o.Money);
                    //已退押金
                    t = model.Where(o => o.DepositType == DepositType.退押金).Sum(o => o.Money);

                    pice = Math.Abs(s - t);
                    break;
                case DepositType.退押金:
                    //已收押金
                    s = model.Where(o => o.DepositType == DepositType.收押金).Sum(o => o.Money);
                    //已退押金
                    t = model.Where(o => o.DepositType == DepositType.退押金 && o.Id != depositId).Sum(o => o.Money);

                    pice = Math.Abs(s - t);

                    break;
                default:
                    break;
            }

            return pice;

        }

        public async Task UpdateStatus(Guid id)
        {
            var model = await _repositoryDeposit.GetAsync(id);
            model.DepositStatus = DepositStatus.已提交;
            await _repositoryDeposit.UpdateAsync(model);
        }

        public async Task UpdateExamineStatus(Guid id)
        {
            var model = await _repositoryDeposit.GetAsync(id);
            model.DepositStatus = DepositStatus.已审核;

            await _repositoryDeposit.UpdateAsync(model);
        }

        public async Task UpdateBackStatus(Guid id)
        {
            var model = await _repositoryDeposit.GetAsync(id);
            model.DepositStatus = DepositStatus.退回;


            await _repositoryDeposit.UpdateAsync(model);
        }

        private Task<List<DepositInfo>> GetCache
        {
            get
            {
                ICache cache = _catchmanager.GetCache(nameof(DepositInfo));
                return cache.GetAsync("list", async () => await _repositoryDeposit.GetAllIncluding(o => o.RestaurantInfoModel).ToListAsync());
            }
        }
    }
}
